﻿namespace Lite.Messaging.Msmq
{
    using System;
    using Lite.Common;
    using System.Messaging;

    internal sealed partial class MsmqQueueMonitor
    {
        private readonly object stateLockObject = new object();

        private readonly MsmqMessageQueue messageQueue;

        private bool isRunning;

        private bool isPaused;

        private bool isPeeking;

        internal MsmqQueueMonitor(MsmqMessageQueue messageQueue)
        {
            this.messageQueue = messageQueue;
        }

        private void OnMessageAvailable()
        {
            EventHandler<MessageQueueEventArgs> messageAvailable = MessageAvailable;

            if (messageAvailable != null)
            {
                messageAvailable(this, new MessageQueueEventArgs(this.messageQueue));
            }
        }

        private void BeginPeek()
        {
            lock (this.stateLockObject)
            {
                if (this.isPeeking || this.isPaused || !this.isRunning)
                {
                    return;
                }
                else
                {
                    this.isPeeking = true;
                }
            }

            messageQueue.MessageQueue.BeginPeek(TimeSpan.FromSeconds(5), null, result =>
                {
                    bool isMessageAvailable;

                    try
                    {
                        messageQueue.MessageQueue.EndPeek(result);

                        isMessageAvailable = true;
                    }
                    catch(MessageQueueException ex)
                    {
                        if (ex.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout)
                        {
                            lock (stateLockObject)
                            {
                                isPeeking = false;
                                isPaused = false;
                                isRunning = false;
                            }
                            throw new MessagingException("The queue monitor stopped due to an internal error.", ex);
                        }

                        isMessageAvailable = false;
                    }

                    lock (stateLockObject)
                    {
                        if (isMessageAvailable && isRunning && !isPaused)
                        {
                            isPaused = true;

                            OnMessageAvailable();
                        }

                        isPeeking = false;
                    }

                    BeginPeek();
                });
        }
    }

    internal sealed partial class MsmqQueueMonitor
        : IQueueMonitor
    {
        public event EventHandler<MessageQueueEventArgs> MessageAvailable;

        public IMessageQueue MessageQueue
        {
            get
            {
                return this.messageQueue;
            }
        }
    }

    internal sealed partial class MsmqQueueMonitor
        : IStartHaltPauseRestart
    {
        public void Pause()
        {
            lock (this.stateLockObject)
            {
                if (!this.isRunning)
                {
                    throw new InvalidOperationException("Cannot pause an object that is not started.");
                }

                isPaused = true;
            }
        }

        public void Restart()
        {
            lock (this.stateLockObject)
            {
                if (!this.isRunning)
                {
                    throw new InvalidOperationException("Cannot resume an object that is not started.");
                }

                isPaused = false;

                BeginPeek();
            }
        }
    }

    internal sealed partial class MsmqQueueMonitor
            : IStartHalt
    {
        public StartHaltState State
        {
            get
            {
                lock (this.stateLockObject)
                {
                    if (this.isRunning)
                    {
                        if (this.isPaused)
                        {
                            return StartHaltState.Paused;
                        }
                        else
                        {
                            return StartHaltState.Started;
                        }
                    }
                    else
                    {
                        if (this.isPeeking)
                        {
                            return StartHaltState.Halting;
                        }
                        else
                        {
                            return StartHaltState.Halted;
                        }
                    }
                }
            }
        }

        public void Start()
        {
            lock (this.stateLockObject)
            {
                this.isRunning = true;
            }

            this.BeginPeek();
        }

        public void Halt()
        {
            lock (this.stateLockObject)
            {
                this.isRunning = false;
                this.isPaused = false;
            }
        }
    }
}
